﻿using AutomatedTestDataManagement.Models;
using AutomatedTestLibrary;
using System.Collections.Specialized;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web.Mvc;
using System;

namespace AutomatedTestDataManagement.Controllers
{
    /// <summary>
    /// Controller for data table records
    /// </summary>
    public class DatumController : Controller
    {
        /// <summary>
        /// Database context to use with model-based queries
        /// </summary>
        /// <permission cref="">Private</permission>
        private ApplicationTestDataEntities db = new ApplicationTestDataEntities();

        /// <summary>
        /// Library for ADO-based queries into virtual tables
        /// </summary>
        private DatabaseLib dblib;

        /// <summary>
        /// List used to refer to column names in virtual tables
        /// </summary>
        private List<string> colnames = new List<string>();
        
        //
        // GET: /Datum/
        /// <summary>
        /// Retrieves the list of all records in the virtual table represented by the id value
        /// </summary>
        /// <param name="id">id of the virtual table to be viewed</param>
        /// <returns>Dictionary containing the data as a view</returns>
        public ActionResult Index(int id = 0)
        {
            ViewBag.TableMasterID = id;
            ViewBag.TableName = db.TableMasters.Find(id).TableName;
            // find the max DataID value for the table in question
            // since EF has fits over dynamic result sets, we have to drop to old-style ADO - use the database library for this
            dblib = setupDbLib();
            var data = dblib.GetDataRows(id);
            if ((data as OrderedDictionary).Count == 0) { return RedirectToAction("NewDataSchema", new { id = id }); }
            return View(data);
        }


        //
        // GET: /Datum/Details/5
        /// <summary>
        /// Details of a single data row. Not used
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult Details(int id = 0)
        {
            Datum datum = db.Data.Find(id);
            if (datum == null)
            {
                return HttpNotFound();
            }
            return View(datum);
        }

        //
        // GET: /Datum/Create
        /// <summary>
        /// Create a new row in the virtual table represented by id
        /// </summary>
        /// <param name="id">id of the virtual table the row belongs to</param>
        /// <returns>redirects to the NewDataSchema action if no rows exist, otherwise returns an empty view with the column names stored in the view bag</returns>
        public ActionResult Create(int id = 0)
        {
            ViewBag.TableMasterID = id;
            ViewBag.TableName = db.TableMasters.Find(id).TableName;
            int maxDataId = db.Data.Where(d => d.TableMasterID == id).Max(d => d.DataID);
            ViewBag.DataID = maxDataId + 1;

            // The view for creating new data must be different if there is no data already in the table
            dblib = setupDbLib();
            OrderedDictionary data = dblib.GetDataRows(id) as OrderedDictionary;
            if (data == null)
            {
                // return a view allowing configuration of data with placeholder values 
                return RedirectToAction("NewDataSchema", new { id = id });
            }
            else
            {
                // return a view based on an existing row
                createDatumView(data);
                ViewBag.ColumnNames = colnames;
                return View();
            }
        }

        //
        // POST: /Datum/Create
        /// <summary>
        /// Responds only to HTTPPOST action to create data
        /// </summary>
        /// <param name="data">Collection of form data</param>
        /// <returns>Redirects to the Index action for the virtual table if creation succeeds, otherwise returns all data to the Create form</returns>
        [HttpPost]
        public ActionResult Create(FormCollection data)
        {
            // because our data is coming via virtual tables, we have to do this the old-fashioned way
            
            Datum datum = null;

            string errMsg = "";
            
            // first pull DataID and TableMasterID - these will be constant
            int dID = Convert.ToInt32(data["DataID"]);
            int tmID = Convert.ToInt32(data["TableMasterID"]);

            
            foreach(string field in data["FieldName"].Split(','))
            {
                datum = new Datum();
                datum.DataID = dID;
                datum.TableMasterID = tmID;
                datum.DataName = field;
                datum.DataValue = data["FieldValue_" + field];
                datum.DataDetails = datum.DataValue;
                if (ModelState.IsValid)
                {
                    db.Data.Add(datum);
                    db.SaveChanges();
                }
                else
                {
                    // need to manually raise validation errors here
                }
            }

            if (errMsg == "")
            {
                return RedirectToAction("Index", new { id = tmID });
            }
            else
            {
                // need to load everything back up to resend.
                ViewBag.DataID = dID;
                ViewBag.TableMasterID = tmID;
                ViewBag.TableName = db.TableMasters.Find(tmID).TableName;
                ViewBag.ColumnNames = data["FieldName"];
                return View(data);
            }
        }

        /// <summary>
        /// Loads table name for virtual table with no data and returns empty view
        /// </summary>
        /// <param name="id">id of the virtual table</param>
        /// <returns>Empty view</returns>
        public ActionResult NewDataSchema(int id = 0)
        {
            ViewBag.TableMasterID = id;
            ViewBag.TableName = db.TableMasters.Find(id).TableName;
            ViewBag.DataID = 1;
            return View();
        }

        /// <summary>
        /// responds to HTTPPOST action only to create the first row of a virtual table
        /// </summary>
        /// <param name="data">Collection of form data</param>
        /// <returns>Redirects to the index view of the table if the save succeeds</returns>
        [HttpPost]
        public ActionResult NewDataSchema(FormCollection data)
        {
            // first pull DataID and TableMasterID - these will be constant
            int dID = Convert.ToInt32(data["DataID"]);
            int tmID = Convert.ToInt32(data["TableMasterID"]);
            string[] values = data["FieldValue"].Split(',');
            string[] fields = data["FieldName"].Split(',');

            for (int idx = 0; idx < values.Length; idx++)
            {
                Datum datum = new Datum();
                datum.DataID = dID;
                datum.TableMasterID = tmID;
                datum.DataName = fields[idx];
                datum.DataValue = values[idx];
                datum.DataDetails = values[idx];

                db.Data.Add(datum);
                db.SaveChanges();
            }

            return RedirectToAction("Index", new { id = tmID });
        }
        
        
        //
        // GET: /Datum/Edit/5
        /// <summary>
        /// Loads the virtual row for the given virtual table
        /// </summary>
        /// <param name="id">id of the virtual row</param>
        /// <param name="tableMasterID">id of the virtual table</param>
        /// <returns>View including virtual row information</returns>
        public ActionResult Edit(int id = 0, int tableMasterID = 0)
        {

            if (tableMasterID == 0) { return RedirectToAction("Index", "Home"); }

            ViewBag.DataID = id;
            setTableMaster(tableMasterID);

            dblib = setupDbLib();
            OrderedDictionary data = dblib.GetDataRow(tableMasterID, id) as OrderedDictionary;
            ViewBag.Data = data;
            return View();
        }

        //
        // POST: /Datum/Edit/5
        /// <summary>
        /// Responds to HTTPPOST action only to update a given virtual data row
        /// </summary>
        /// <param name="data">Collection of form fields representing the virtual row</param>
        /// <returns>Redirects to the index view of the virtual table</returns>
        [HttpPost]
        public ActionResult Edit(FormCollection data)
        {
            // first pull DataID and TableMasterID - these will be constant
            int dID = Convert.ToInt32(data["DataID"]);
            int tmID = Convert.ToInt32(data["TableMasterID"]);
            string[] names = data["DataName"].Split(',');

            foreach(string name in names)
            {
                Datum datum = new Datum();
                datum.TableMasterID = tmID;
                datum.DataID = dID;
                datum.DataName = name;
                datum.DataValue = data[name];
                datum.DataDetails = data[name];

                // locate the unique id of the underlying data record
                var datumID = db.Data.Where(d => d.DataID == dID && d.TableMasterID == tmID && d.DataName == name).Select(d=>d.DataTableID).Single();

                datum.DataTableID = datumID;

                    db.Entry(datum).State = EntityState.Modified;
                    db.SaveChanges();
            }

            return RedirectToAction("Index", new { id = tmID });
        }

        //
        // GET: /Datum/Delete/5
        /// <summary>
        /// Loads details of a given virtual row in a virtual table
        /// </summary>
        /// <param name="id">id of the virtual row</param>
        /// <param name="tableMasterID">id of the virtual table</param>
        /// <returns>View of the virtual data record to be deleted. Deletion is not allowed if the record is in use</returns>
        public ActionResult Delete(int id = 0, int tableMasterID = 0)
        {
            if (tableMasterID == 0) { return RedirectToAction("Index", "Home"); }

            ViewBag.DataID = id;
            setTableMaster(tableMasterID);

            dblib = setupDbLib();

            OrderedDictionary data = dblib.GetDataRow(tableMasterID, id) as OrderedDictionary;
            var ids = db.Data.Where(d => d.DataID == id && d.TableMasterID == tableMasterID).Select(d => d.DataTableID);
            ViewBag.Data = data;
            ViewBag.DataTableIDs = ids;
            dblib.CloseConnection();
            string tableName = db.TableMasters.Where(tm => tm.TableMasterID == tableMasterID).Select(tm => tm.TableName).Single();

            var testSteps = db.TestSteps.Where(ts => ts.DataID == id && ts.StepKind.DataTable == tableName);
            if (testSteps.Count() > 0)
            {
                ModelState.AddModelError("", "This data record is used by one or more Test Steps. It cannot be deleted while it is in use");
                ViewBag.TestSteps = testSteps;
            }
            return View();
        }

        //
        // POST: /Datum/Delete/5
        /// <summary>
        /// Responds to HTTPPOST action only to delete the given virtual row
        /// </summary>
        /// <param name="data">Collection of form data</param>
        /// <returns>Redirects to the index view of the virtual table</returns>
        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(FormCollection data)
        {
            int dID = Convert.ToInt32(data["DataID"]);
            int tmID = Convert.ToInt32(data["TableMasterID"]);

            string[] recs = data["DataTableID"].Split(',');

            foreach (string id in recs)
            {
                Datum datum = db.Data.Find(Convert.ToInt32(id));
                db.Data.Remove(datum);
                db.SaveChanges();
            }
            return RedirectToAction("Index", new { id = tmID } );
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }

        /// <summary>
        /// Create an instance of the ODBC handler
        /// </summary>
        /// <returns>DatabaseLib instance</returns>
        /// <permission cref="">Private</permission>
        /// <seealso cref="AutomatedTestLibrary.DatabaseLib"/>
        private DatabaseLib setupDbLib()
        {
            // wrapper to reduce duplicate code.
            dblib = new DatabaseLib();
            dblib.InitializeDefaults();
            return dblib;

        }

        /// <summary>
        /// Load the list of virtual column names
        /// </summary>
        /// <param name="data">Dictionary containing virtual rows</param>
        /// <permission cref="">Private</permission>
        private void createDatumView(OrderedDictionary data)
        {
            foreach (string key in (data[0] as OrderedDictionary).Keys)
            {
                this.colnames.Add(key);
            }
        }

        /// <summary>
        /// Set the virtual table ID and name
        /// </summary>
        /// <param name="tableMasterID">Virtual table ID</param>
        /// <permission cref="">Private</permission>
        private void setTableMaster(int tableMasterID)
        {
            this.ViewBag.TableMasterID = tableMasterID;
            this.ViewBag.TableName = db.TableMasters.Find(tableMasterID).TableName;
        }
    }
}